﻿using S7Cat.Enums;
using S7Cat.Models;

namespace S7Cat.Tools
{
    public static class AddressParser
    {
        public static DataItemAddress Parse(string address)
        {
            DataType dataType = DataType.DataBlock;
            ushort db = 0,
                startAdr = 0,
                len = 1;
            byte bitAdr = 255;

            var type = address.AsSpan(0, 2);
            switch (type)
            {
                case "DB":
                    dataType = DataType.DataBlock;
                    Span<Range> ranges = stackalloc Range[3];

                    var content = address.AsSpan(2);

                    int count = content.Split(ranges, '.');
                    if (count < 2)
                    {
                        throw new InvalidDataException("To few periods for DB address");
                    }

                    db = ushort.Parse(content[ranges[0]]);
                    startAdr = ushort.Parse(content[ranges[1]].Slice(3));

                    switch (content[ranges[1]][2])
                    {
                        case 'B': //DBB
                            break;
                        case 'W': //DBW
                            len = 2;
                            break;
                        case 'D': //DBD
                            len = 4;
                            break;
                        case 'X': //DBX                            
                            bitAdr = byte.Parse(content[ranges[2]]);
                            if (bitAdr > 7)
                                throw new InvalidDataException("Bit can only be 0-7");
                            break;
                    }
                    break;
                case "IB":
                case "EB":
                    dataType = DataType.Input;
                    startAdr = ushort.Parse(address.AsSpan(2));
                    break;
                case "IW":
                case "EW":
                    dataType = DataType.Input;
                    startAdr = ushort.Parse(address.AsSpan(2));
                    len = 2;
                    break;
                case "ID":
                case "ED":
                    dataType = DataType.Input;
                    startAdr = ushort.Parse(address.AsSpan(2));
                    len = 4;
                    break;
                case "QB":
                case "AB":
                case "OB":
                    dataType = DataType.Output;
                    startAdr = ushort.Parse(address.AsSpan(2));
                    break;
                case "QW":
                case "AW":
                case "OW":
                    dataType = DataType.Output;
                    startAdr = ushort.Parse(address.AsSpan(2));
                    len = 2;
                    break;
                case "QD":
                case "AD":
                case "OD":
                    dataType = DataType.Output;
                    startAdr = ushort.Parse(address.AsSpan(2));
                    len = 4;
                    break;
                case "MB":
                    dataType = DataType.Memory;
                    startAdr = ushort.Parse(address.AsSpan(2));
                    break;
                case "MW":
                    dataType = DataType.Memory;
                    startAdr = ushort.Parse(address.AsSpan(2));
                    len = 2;
                    break;
                case "MD":
                    dataType = DataType.Memory;
                    startAdr = ushort.Parse(address.AsSpan(2));
                    len = 4;
                    break;
                default:
                    switch (address[0])
                    {
                        case 'E':
                        case 'I':
                            dataType = DataType.Input;
                            break;
                        case 'Q':
                        case 'A':
                        case 'O':
                            dataType = DataType.Output;
                            break;
                        case 'M':
                            dataType = DataType.Memory;
                            break;
                        case 'T':
                            dataType = DataType.Timer;
                            startAdr = ushort.Parse(address.AsSpan(1));
                            len = 2;
                            break;
                        case 'Z':
                        case 'C':
                            dataType = DataType.Counter;
                            startAdr = ushort.Parse(address.AsSpan(1));
                            len = 2;
                            break;
                        default:
                            throw new InvalidDataException(
                                string.Format("{0} is not a valid address", address[0])
                            );
                    }

                    content = address.AsSpan(1);

                    var index = content.IndexOf('.');
                    if (index == -1)
                    {
                        throw new InvalidDataException("To few periods for DB address");
                    }
                    else
                    {
                        startAdr = ushort.Parse(content.Slice(0, index));
                        bitAdr = byte.Parse(content.Slice(index + 1));

                        if (bitAdr > 7)
                            throw new InvalidDataException("Bit can only be 0-7");
                    }

                    break;
            }

            //var item = new DataItemAddress(dataType, db, startAdr, len, bitAdr);
            //_addressCache[address] = item;
            //return item.Clone();
            return new DataItemAddress(dataType, db, startAdr, len, bitAdr);
        }
    }
}
